#include <iostream>
#include <iomanip>
#include <vector>
#include <map>
#include <string>
#include <regex>
#include <algorithm>

#include "DemoString.h"

using namespace std;

// 数组 --'分隔符'--> 字符串
string componentsJoinedByString(const vector<string>& strArr, const string& sepStr) {
    string rtn = "";
    if (strArr.size() == 0) {
        return rtn;
    }
    vector<string>::const_iterator it = strArr.begin();
    rtn += *it;
    for(++it; it != strArr.end(); ++it){
        rtn += sepStr;
        rtn += *it;
    }
    return rtn;
}

// 字符串 --'分隔符'--> 数组
vector<string> componentsSeparatedByString(const string& str, const string& sepStr) {
    vector<string> rtn;
    if (0 == sepStr.length()) {
        rtn.push_back(str);
        return rtn;
    }
    
    string::size_type fromIndex = 0;
    string::size_type sepIndex = str.find(sepStr, fromIndex);
    
    while (sepIndex != string::npos) {
        rtn.push_back(str.substr(fromIndex, sepIndex-fromIndex));
        fromIndex = sepIndex;
        fromIndex += sepStr.length();
        
        sepIndex = str.find(sepStr, fromIndex);
    }
    rtn.push_back(str.substr(fromIndex));
    
    return rtn;
}

// 字符串 --'分隔符'--> 数组, 使用正则表达式 api
vector<string> componentsSeparatedByStringRegex(const string& str, const string& sepStr) {
    vector<string> rtn;
    if (0 == sepStr.length()) {
        rtn.push_back(str);
        return rtn;
    }
    
    // https://zhuanlan.zhihu.com/p/112219748
    regex reg(sepStr);
    // regex_token_iterator 迭代的是所有正则表达式匹配中的指定子表达式，或迭代未匹配的子字符串
    
    // sregex_token_iterator 第 4 个参数表示第几个 group,
    // 如果我们把这里的参数设为 -1，则迭代字符串中所有不匹配正则表达式的部分，相当于用正则表达式切割字符串
    sregex_token_iterator pos(str.cbegin(),
                              str.cend(),
                              reg,
                              -1);
    
    sregex_token_iterator end;
    
    for (; pos!=end; pos++) {
        rtn.push_back(string(*pos));
    }
    
    return rtn;
}

void stringFromToArray(void) {
    cout<<endl<<endl<<"stringFromToArray"<<endl;
    
    vector<string> monthArr {
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December"
    };
    
    // 数组 --'分隔符'--> 字符串
    string joinedStr = componentsJoinedByString(monthArr, "--");
    cout<<"joinedStr : "<<joinedStr<<endl;
    //joinedStr = string("--")+joinedStr;
    
    // 字符串 --'分隔符'--> 数组
    vector<string> strArr = componentsSeparatedByString(joinedStr, "-");
    for (auto str : strArr) {
        cout<<"strArr : "<<str<<endl;
    }
    
    vector<string> strArr2 = componentsSeparatedByString(joinedStr, "er");
    for (auto str : strArr2) {
        cout<<"strArr2 : "<<str<<endl;
    }
    
    // 字符串 --'分隔符'--> 数组 by 正则api
    vector<string> strArr3 = componentsSeparatedByStringRegex(joinedStr, "e");
    for (auto str : strArr3) {
        cout<<"regex=e, strArr3 : "<<str<<endl;
    }
}

// origin 查找所有 findStr， 替换为 replaceTobe
string replaceAll(const string& origin, const string&  findStr, const string& replaceTobe) {
    string rtn = origin;
    
    if (findStr.length() == 0) {
        return rtn;
    }
    
    string::size_type fromIndex = 0;
    string::size_type posIndex = rtn.find(findStr, fromIndex);
    
    while (posIndex != string::npos) {
        rtn.replace(posIndex, findStr.length(), replaceTobe);
        fromIndex = posIndex;
        fromIndex += replaceTobe.length();
        
        posIndex = rtn.find(findStr, fromIndex);
    }
    
    return rtn;
}

void subStringSearch(void) {
    cout<<endl<<endl<<"subStringSearch"<<endl;
    
    string ostr = "浔阳江头夜送客，枫叶荻花秋瑟瑟。主人下马客在船，举酒欲饮无管弦。醉不成欢惨将别，别时茫茫江浸月。忽闻水上琵琶声，主人忘归客不发。寻声暗问弹者谁，琵琶声停欲语迟。移船相近邀相见，添酒回灯重开宴。千呼万唤始出来，犹抱琵琶半遮面。转轴拨弦三两声，未成曲调先有情。弦弦掩抑声声思，似诉平生不得志。低眉信手续续弹，说尽心中无限事。轻拢慢捻抹复挑，初为《霓裳》后《六幺》。大弦嘈嘈如急雨，小弦切切如私语。嘈嘈切切错杂弹，大珠小珠落玉盘。间关莺语花底滑，幽咽泉流冰下难。冰泉冷涩弦凝绝，凝绝不通声暂歇。别有幽愁暗恨生，此时无声胜有声。银瓶乍破水浆迸，铁骑突出刀枪鸣。曲终收拨当心画，四弦一声如裂帛。东船西舫悄无言，唯见江心秋月白。沉吟放拨插弦中，整顿衣裳起敛容。自言本是京城女，家在虾蟆陵下住。十三学得琵琶成，名属教坊第一部。曲罢曾教善才服，妆成每被秋娘妒。五陵年少争缠头，一曲红绡不知数。钿头银篦击节碎，血色罗裙翻酒污。今年欢笑复明年，秋月春风等闲度。弟走从军阿姨死，暮去朝来颜色故。门前冷落鞍马稀，老大嫁作商人妇。商人重利轻别离，前月浮梁买茶去。去来江口守空船，绕船月明江水寒。夜深忽梦少年事，梦啼妆泪红阑干。我闻琵琶已叹息，又闻此语重唧唧。同是天涯沦落人，相逢何必曾相识！我从去年辞帝京，谪居卧病浔阳城。浔阳地僻无音乐，终岁不闻丝竹声。住近湓江地低湿，黄芦苦竹绕宅生。其间旦暮闻何物？杜鹃啼血猿哀鸣。春江花朝秋月夜，往往取酒还独倾。岂无山歌与村笛？呕哑嘲哳难为听。今夜闻君琵琶语，如听仙乐耳暂明。莫辞更坐弹一曲，为君翻作《琵琶行》。感我此言良久立，却坐促弦弦转急。凄凄不似向前声，满座重闻皆掩泣。座中泣下谁最多？江州司马青衫湿。";
     
    // 从头到尾 查找第一处
    string::size_type firstPos = ostr.find("琵琶");
    if(firstPos != string::npos) {
        string fstr = ostr.substr(0, firstPos);
        cout<<"fstr : "<<fstr<<endl;
    }
     
    // 从末尾向开头 查找第一处
    string::size_type tailPos = ostr.rfind("琵琶");
    if(tailPos != string::npos) {
        string tstr = ostr.substr(tailPos);
        cout<<"tstr : "<<tstr<<endl;
    }
     
    // 替换所有命中的 琵琶 为 PiPa
    string rpStr = replaceAll(ostr, "琵琶", "PiPa");
    cout<<"所有 琵琶 --> PiPa : "<<rpStr<<endl;

    // 替换第一处 琵琶 为 PiPa
    string::size_type posIndex = ostr.find("琵琶");
    if (posIndex != string::npos) {
        string strCopy = ostr;
        strCopy.replace(posIndex, string("琵琶").length(), "PiPa");
        cout<<"第一处 琵琶 --> PiPa : "<<strCopy<<endl;
    }
    
}

vector<string> regexSearchAll(const string& ostr, const string& regStr) {
    vector<string> rtn;
    if (regStr.length() == 0) {
        return rtn;
    }
    
    smatch m;
    regex regexObj(regStr);
    string searchStr = ostr;
    
    // search + 提取(捕捉), 找到第一个后就结束。
    bool isFound = regex_search(searchStr, m, regexObj);
    
    while(isFound) {
        // m.str(0)一定是整个正则匹配到的部分
        rtn.push_back(string(m.str(0)));
        
        // m.suffix().str()获取整个匹配之后的字符串
        searchStr = m.suffix().str();
        if (searchStr.length() == 0) {
            break;
        }
        
        // 继续 search + 提取(捕捉)
        isFound = regex_search(searchStr, m, regexObj);
    }
    
    return rtn;
}

vector<string> regexSearchAllByIterator(const string& ostr, const string& regStr) {
    vector<string> rtn;
    if (regStr.length() == 0) {
        return rtn;
    }
    // 使用 sregex_iterator 捕捉所有的 正则匹配
    regex regexObj(regStr);
    sregex_iterator pos(ostr.cbegin(), ostr.cend(), regexObj);
    sregex_iterator end;
    
    for(; pos!=end; ++pos) {
        // pos->str() 是捕捉到的整个匹配字符串，pos->str(1) 是捕捉到的 分组1, pos->str(2)是捕捉到的 分组2,...
        string matchStr(pos->str());
        if (matchStr.length() > 0) {
            rtn.push_back(matchStr);
        }
    }
    
    return rtn;
}

vector<string> regexSearchAllByTokenIterator(const string& ostr, const string& regStr) {
    vector<string> rtn;
    if (regStr.length() == 0) {
        return rtn;
    }
    
    // 使用 sregex_iterator 捕捉所有的 正则匹配
    regex regexObj(regStr);
    // 最后一个参数为0 表示 整个匹配串，1，表示第一个分组; -1 表示 匹配串之外的部分，可以用来 分割字符串
    sregex_token_iterator pos(ostr.cbegin(), ostr.cend(), regexObj, 0);
    sregex_token_iterator end;
    
    for(; pos!=end; ++pos) {
        string matchStr(pos->str());
        if (matchStr.length() > 0) {
            rtn.push_back(matchStr);
        }
    }
    
    return rtn;
}

void regexDemo(void) {
    cout<<endl<<endl<<"regexDemo"<<endl;
/*
    C++11 自带了 6 种正则表达式语法的支持

    ECMAScript
    basic
    extended
    awk
    grep
    egrep
    C++11 默认使用 ECMAScript 语法，这也是 6 种语法中最强大的，假如想使用其他 5 种语法，只需在声明 regex 对象时指定即可
    

 ECMAScript支持的正则
 正则由元字符和普通字符组成。普通字符就代表它原本的含义；元字符的意义不同于该字符本来的含义，而是有特殊的意义和功能。

 根据其意义功能划分，可将元字符划分为：

 具有特殊意义的元字符
 \：\字符能够改变字符原本的含义

 ^：^字符指示字符串的头，且要求字符串以字符开头，不占位。\^表示一个真正的^符号。

 $：$字符指示字符串的尾，且要求字符串以字符结尾，不占位。\$表示一个真正的$符号。

 、()：分组，大正则中包含小正则。可以改变默认的优先级。在模式中可以使用\1来表示第一组已然捕获到的东西。

 \b：指示字符串的边界（头/尾/空格左/空格右），字符\b要求边界的左边是字符，\b字符要求边界的右边是字符。

 .：表示一个除了\n以外的任意一个字符。\.表示一个真正的.符号。

 |：字符串1|字符串2表示一个字符串，该字符串是字符串1、字符串2中的一个。|在正则中的优先级比较混乱，所以建议加上足够多的括号来分组。

 []：[字符1字符2字符3...]表示一个字符，该字符是字符1、字符2、字符3……中的某一个。中括号中出现的所有字符都是代表本身意思的字符（没有特殊含义），如[.]只能匹配.符号，而不能匹配任意符号。

 [^字符1字符2字符3...]表示一个字符，该字符不是字符1、字符2、字符3……中的任何一个

 [a-z]表示一个字符，该字符是a、b、c……z中的某一个

 [^a-z]表示一个字符，该字符不是a、b、c……z中的任何一个
 
 \w：表示一个字符，该字符是数字、字母、下划线中的某一个。等价于[(0-9)(a-z)(A-Z)(_)]

 \W：表示一个字符，该字符不是数字、字母、下划线中的任何一个。等价于[]

 \d表示一个字符，该字符是0、1、2……9中的某一个

 \D表示一个字符，该字符不是0、1、2……9中的任何一个

 \s表示一个字符，该字符是空白符（空格、制表符、换页符）2、代表出现次数的

 量词元字符
 *：字符*要求字符出现0到多次

 +：字符+要求字符出现1到多次

 ?：字符?要求字符出现0次或1次

 {n}：字符{n}要求字符出现n次

 {n,}：字符{n,}要求字符出现n到多次

 {n,m}：字符{n,m}要求字符出现n到m次、
 
 https://blog.csdn.net/bgzclxqq/article/details/90262904
 */

    string ostr = "百科熊猫词条  https://baike.baidu.com/item/%E5%A4%A7%E7%86%8A%E7%8C%AB/34935?fromtitle=%E7%86%8A%E7%8C%AB&fromid=162918&fr=aladdin http://127.0.0.1:8080/dokuwiki/doku.php?id=cpp-str2arr panda@163.com 大熊猫（学名：Ailuropoda melanoleuca）：属于食肉目、熊科、大熊猫亚科和大熊猫属唯一的哺乳动物，头躯长1.2-1.8米，尾长10-12厘米。体重80-120千克，最重可达180千克，体色为黑白两色，它有着圆圆的脸颊，大大的黑眼圈，胖嘟嘟的身体，标志性的内八字的行走方式，也有解剖刀般锋利的爪子。是世界上最可爱的动物之一。  panda@gmail.com  com.panda@gmail (010)88886666 ，或 022-22334455，或 02912345678 511122198812068932 41112219881206893X 18660000062 17000000026 ";
    cout<<"ostr:"<<ostr<<endl;
    
    
    // 身份证
    string idPattern = "\\b(\\d{17}|\\d{14})(\\d|[xX])\\b";
    // 电话号码：eg：021-68686868  0511-6868686；
    string phonePattern = "\\b(\\d{3,4}-)\\d{7,8}\\b";
    // 手机
    string mobilePhonePattern = "\\b1\\d{10}\\b";
    // 邮箱
    string emailPattern = "\\b[a-zA-Z0-9]\\w*@[a-zA-Z0-9]+\\.[a-zA-Z]{2,4}\\b";
    // url
    string urlPattern = "(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]";
    // 中文字符 不work
    //string cnPattern = "[\u4e00-\u9fa5]+";
    
    
    // match, 都会返回false，因为 ostr 都含有 正则表达式没有覆盖的部分
    bool isMatch = regex_match(ostr, regex(idPattern));
    cout<<"身份证 "<<idPattern<<", regex_match: "<<isMatch<<endl;
    isMatch = regex_match(ostr, regex(phonePattern));
    cout<<"座机 regex_match "<<isMatch<<endl;
    isMatch = regex_match(ostr, regex(mobilePhonePattern));
    cout<<"手机 regex_match "<<isMatch<<endl;
    isMatch = regex_match(ostr, regex(emailPattern));
    cout<<"邮箱 regex_match "<<isMatch<<endl;
    isMatch = regex_match(ostr, regex(urlPattern));
    cout<<"url regex_match "<<isMatch<<endl;
    //isMatch = regex_match(ostr, regex(cnPattern));
    //cout<<"中文 regex_match "<<isMatch<<endl;
    
    
    // search 但不提取, 都会返回true，因为 ostr 都含有 正则表达式 能够覆盖的部分
    bool isFound = regex_search(ostr, regex(idPattern));
    cout<<"身份证 "<<idPattern<<", regex_search: "<<isFound<<endl;
    isFound = regex_search(ostr, regex(phonePattern));
    cout<<"座机 "<<phonePattern<<",  regex_search "<<isFound<<endl;
    isFound = regex_search(ostr, regex(mobilePhonePattern));
    cout<<"手机 "<<mobilePhonePattern<<",  regex_search "<<isFound<<endl;
    isFound = regex_search(ostr, regex(emailPattern));
    cout<<"邮箱 "<<emailPattern<<",  regex_search "<<isFound<<endl;
    isFound = regex_search(ostr, regex(urlPattern));
    cout<<"url "<<urlPattern<<",  regex_search "<<isFound<<endl;
    //isFound = regex_match(ostr, regex(cnPattern));
    //cout<<"中文 regex_search "<<isFound<<endl;
    
    
    // 搜索第一处匹配
    // 使用reg_search函数匹配，用smatch的实例存储匹配的结果，即完成捕捉。模式中一般要有分组（因为捕捉的正是分组匹配的结果）
    // 定义一个STL容器smatch，用来保存捕捉的结果
    smatch m;
    cout<<"search + 提取(捕捉) "<<urlPattern<<endl;
    
    isFound = regex_search(ostr, m, regex(urlPattern));
    if(isFound) {
        // 使用m.size()，查看捕捉到的个数；使用m.str(i)，查看捕捉到的字符串；
        //      注意：m.str(0)一定是整个正则匹配到的部分，m.str(1)及以后才是分组捕获的结果】

        cout << "m.size() " << m.size() << endl;
        for(int i=0; i<m.size(); ++i){
            cout << "m.str(" << i << "): " << m.str(i) << endl;
        }
        
        //    m.prefix().str()获取整个匹配之前的字符串；m.suffix().str()获取整个匹配之后的字符串
        cout << "m.prefix().str(): " << m.prefix().str() << endl;
        cout << "m.suffix().str(): " << m.suffix().str() << endl;
    }
    else {
        cout << "Not Found" << endl;
    }
    
    
    // 3种方法，搜索全部的匹配
    vector<string> res1 = regexSearchAll(ostr, idPattern);
    for (auto itr : res1) {
        cout<<"regexSearchAll, 身份证 "<<idPattern<<" -> "<<itr<<endl;
    }
    
    vector<string> res2 = regexSearchAll(ostr, phonePattern);
    for (auto itr : res2) {
        cout<<"regexSearchAll, 座机 "<<phonePattern<<" -> "<<itr<<endl;
    }
    
    vector<string> res5 = regexSearchAll(ostr, urlPattern);
    for (auto itr : res5) {
        cout<<"regexSearchAll, url "<<urlPattern<<" -> "<<itr<<endl;
    }
    
    vector<string> res3 = regexSearchAllByIterator(ostr, mobilePhonePattern);
    for (auto itr : res3) {
        cout<<"regexSearchAllByIterator, 手机 "<<mobilePhonePattern<<" -> "<<itr<<endl;
    }
    
    vector<string> res4 = regexSearchAllByTokenIterator(ostr, emailPattern);
    for (auto itr : res4) {
        cout<<"regexSearchAllByTokenIterator, 邮箱 "<<emailPattern<<" -> "<<itr<<endl;
    }
    
    
    // 正则查询 替换
    string emailGroupPattern = "\\b([a-zA-Z0-9]\\w*)@([a-zA-Z0-9]+)\\.([a-zA-Z]{2,4})\\b";
    // 整个匹配 替换
    string replacedStr = regex_replace(ostr, regex(emailGroupPattern), "____EMAIL____");
    cout<<"regex_replace, 邮箱 "<<emailGroupPattern<<" -> "<<replacedStr<<endl;
    // 带格式使用分组的 替换
    string replacedStr2 = regex_replace(ostr, regex(emailGroupPattern), "$1@gggggggg.$3");
    cout<<"regex_replace2, 邮箱 "<<emailGroupPattern<<" -> "<<replacedStr2<<endl;
    // 带格式使用分组 使用flag的 替换
    //format_no_copy    不要将不匹配的字符串复制到 std::regex_replace 的输出中
    string replacedStr3 = regex_replace(ostr, regex(emailGroupPattern), "$1@gggggggg.$3, ",
                                        regex_constants::format_no_copy);
    cout<<"regex_replace3, 邮箱 "<<emailGroupPattern<<" -> "<<replacedStr3<<endl;
    //format_first_only    仅替换 std::regex_replace 的第一个匹配项
    string replacedStr4 = regex_replace(ostr, regex(emailGroupPattern), "$1@gggggggg.$3",
                                        regex_constants::format_no_copy|regex_constants::format_first_only);
    cout<<"regex_replace4, 邮箱 "<<emailGroupPattern<<" -> "<<replacedStr4<<endl;
}

void stringJoin(void) {
    cout<<endl<<endl<<"stringJoin"<<endl;
 
    string part1 = "七律·长征";
    string part2 = "毛泽东";
    string part3 = "1935年10月";
    string part4 = "红军不怕远征难，万水千山只等闲。";
    string part5 = "五岭逶迤腾细浪，乌蒙磅礴走泥丸。";
    string part6 = "金沙水拍云崖暖，大渡桥横铁索寒。";
    string part7 = "更喜岷山千里雪，三军过后尽开颜。";
    
    cout<<"operator + : "<<(part1+part2+part3+part4+part5+part6+part7)<<endl;
    
    string strJoin = part1;
    strJoin += "\n";
    strJoin += part2;
    strJoin += "\n";
    strJoin += part3;
    strJoin += "\n";
    strJoin += part4;
    strJoin += "\n";
    strJoin += part5;
    strJoin += "\n";
    strJoin += part6;
    strJoin += "\n";
    strJoin += part7;
    
    cout<<"operator +=, strJoin : "<<strJoin<<endl;
    
}

string trimLeft(const string& str, const string& spaceChars) {
    string rtn(str);
    
    string::iterator firstNotSpace = find_if(rtn.begin(),
                                             rtn.end(),
                                             [&spaceChars](unsigned char ch) {
        if(spaceChars.length()==0) {
            // 如果不是 space 字符，返回true，表示找到符合
            return !isspace(ch);
        } else {
            // 如果ch 在 spaceChars 中 找不到，返回true，表示找到符合
            return spaceChars.find(ch) == string::npos;
        }
    });
    
    if (firstNotSpace != rtn.end()) {
        // 找到了
        rtn.erase(rtn.begin(), firstNotSpace);
    }
    
    return rtn;
}

string trimRight(const string& str, const string& spaceChars) {
    string rtn(str);
    
    string::reverse_iterator lastNotSpace = find_if(rtn.rbegin(),
                                             rtn.rend(),
                                             [&spaceChars](unsigned char ch) {
        if(spaceChars.length()==0) {
            // 如果不是 space 字符，返回true，表示找到符合
            return !isspace(ch);
        } else {
            // 如果ch 在 spaceChars 中 找不到，返回true，表示找到符合
            return spaceChars.find(ch) == string::npos;
        }
    });
    
    if (lastNotSpace != rtn.rend()) {
        // 找到了
        rtn.erase(lastNotSpace.base(), rtn.end());
    }
    
    return rtn;
}

string trimLeftAndRight(const string& str, const string& spaceChars) {
    return trimLeft(trimRight(str, spaceChars), spaceChars);
}

void stringTrimDemo(void) {
    cout<<endl<<endl<<"stringTrimDemo"<<endl;
    
    string ostr = " \t 2sapce+1tab 山外青山楼外楼 3space   ";
    string trimstr = trimLeftAndRight(ostr);
    string leftTrimstr = trimLeft(ostr);
    string rightTrimstr = trimRight(ostr);
    
    cout<<"ostr        :|"<<ostr<<"|"<<endl;
    cout<<"trimstr     :|"<<trimstr<<"|"<<endl;
    cout<<"leftTrimstr :|"<<leftTrimstr<<"|"<<endl;
    cout<<"rightTrimstr:|"<<rightTrimstr<<"|"<<endl;
    
    cout<<endl<<"only trim 空格"<<endl;
    
    trimstr = trimLeftAndRight(ostr, " ");
    leftTrimstr = trimLeft(ostr, " ");
    rightTrimstr = trimRight(ostr," ");
    
    cout<<"ostr        :|"<<ostr<<"|"<<endl;
    cout<<"trimstr     :|"<<trimstr<<"|"<<endl;
    cout<<"leftTrimstr :|"<<leftTrimstr<<"|"<<endl;
    cout<<"rightTrimstr:|"<<rightTrimstr<<"|"<<endl;
}

void stringFormat(void) {
    cout<<endl<<endl<<"stringFormat"<<endl;
    
    /*
     http://c.biancheng.net/view/7578.html
     http://t.zoukankan.com/BlairGrowing-p-12810917.html
     
在头文件 iomanip 中定义的C++ 流操纵算子 如下表，带*的表示 流默认值
     
     
*dec                    以十进制形式输出整数
hex                     以十六进制形式输出整数
oct                     以八进制形式输出整数
fixed                   以普通小数形式输出浮点数
scientific              以科学计数法形式输出浮点数
left                    左对齐，即在宽度不足时将填充字符添加到右边
*right                  右对齐，即在宽度不足时将填充字符添加到左边
setbase(b               设置输出整数时的进制，b=8、10 或 16
setw(w)                 指定输出宽度为 w 个字符，或输人字符串时读入 w 个字符
setfill(c)              在指定输出宽度的情况下，输出的宽度不足时用字符 c 填充（默认情况是用空格填充）
setprecision(n)         设置输出浮点数的精度为 n。
                            在使用非 fixed 且非 scientific 方式输出的情况下，n 即为有效数字最多的位数，如果有效数字位数超过 n，则小数部分四舍五人，或自动变为科学计 数法输出并保留一共 n 位有效数字。
                            在使用 fixed 方式和 scientific 方式输出的情况下，n 是小数点后面应保留的位数。
setiosflags(flag)       将某个输出格式标志置为 1
resetiosflags(flag)     将某个输出格式标志置为 0
                            ios::left       输出数据在本域宽范围内向左对齐
                            ios::right      输出数据在本域宽范围内向右对齐
                            ios::internal   数值的符号位在域宽内左对齐，数值右对齐，中间由填充字符填充
                            ios::dec        设置整数的基数为 10
                            ios::oct        设置整数的基数为 8
                            ios::hex        设置整数的基数为 16
                            ios::showbase   强制输出整数的基数（八进制数以 0 开头，十六进制数以 0x 打头）
                            ios::showpoint  强制输出浮点数的小点和尾数 0
                            ios::uppercase  在以科学记数法格式 E 和以十六进制输出字母时以大写表示
                            ios::showpos    对正数显示“+”号
                            ios::scientific 浮点数以科学记数法格式输出
                            ios::fixed      浮点数以定点格式（小数形式）输出
                            ios::unitbuf    每次输出之后刷新所有的流
                            ios::stdio      每次输出之后清除 stdout, stderr
boolalpha               把 true 和 false 输出为字符串
*noboolalpha            把 true 和 false 输出为 0、1
showbase                输出表示数值的进制的前缀
*noshowbase             不输出表示数值的进制.的前缀
showpoint               总是输出小数点
*noshowpoint            只有当小数部分存在时才显示小数点
showpos                 在非负数值中显示 +
*noshowpos              在非负数值中不显示 +
*skipws                 输入时跳过空白字符
noskipws                输入时不跳过空白字符
uppercase               十六进制数中使用 A~E。若输出前缀，则前缀输出 0X，科学计数法中输出 E
*nouppercase            十六进制数中使用 a~e。若输出前缀，则前缀输出 0x，科学计数法中输出 e。
internal                数值的符号（正负号）在指定宽度内左对齐，数值右对 齐，中间由填充字符填充。
     
     */
    int n = 141;
    long ln = 65536L * 65535L * 4;
    //1) 分别以十六进制、十进制、八进制先后输出 n
    cout << "1)  hex->" << hex << n << ", dec->" << dec << n << ", oct->" << oct << n << endl;                              // 1)  hex->8d, dec->141, oct->215
    // 这些进制变换算子设置后，在cout语句结束后依然有效
    cout << "1.1)hex->" <<showbase<<uppercase<< hex<<ln<<endl;                          // 1.1)hex->0X3FFFC0000, dec->17179607040, oct->0177777000000, 0215
    // setiosflags case
    cout << "1.2)dec->"<<setiosflags(ios::dec)<<ln<<", "<<123456<<", "<<setiosflags(ios::showpos)<<123456<<endl;                          // 1.2)0177777000000, 0361100, 123456
    cout << "1.3)"<<boolalpha<<(16 == (1<<4))<<endl;                                    // 1.3)true
    
    double x = 1234567.89, y = 12.34567;
    //2)保留5位有效数字
    cout << "2)  " << setprecision(5) << x << " " << y << " " << endl;                //2)1.2346e+06     12.346
    //3)保留小数点后面5位
    cout << "3)  " << fixed << setprecision(5) << x << " " << y << endl;              //3)1234567.89000    12.34567
    //4)科学计数法输出，且保留小数点后面5位
    cout << "4)  " << scientific << setprecision(5) << x << " " << y << endl;         //4)1.23457e+06     1.23457e+01
    
    // setw() 算子所起的作用是一次性的，即只影响下一次输出。每次需要指定输出宽度时都要使用 setw()
    //5)非负数显示正号，输出宽度为12字符，宽度不足则用 * 填补
    cout << "5)  " << showpos << fixed << setw(12) << setfill('*') << 12.1 << endl;   //5)***+12.10000
    //6)非负数不显示正号，输出宽度为12字符，宽度不足则右边用填充字符填充
    cout << "6)  " << noshowpos << setw(12) << left << 12.1 << endl;                  //6)12.10000****
    //7)输出宽度为 12 字符，宽度不足则左边用填充字符填充
    cout << "7)  " << setw(12) << right << 12.1 << endl;                              //7)****12.10000
    //8)宽度不足时，负号和数值分列左右，中间用填充字符填充
    cout << "8)  " << setw(12) << internal << -12.1 << endl;                          //8)-***12.10000
    cout << "9)  " << 12.1 << endl;                                                   //9)12.10000

}
